/*
 * A client class use for TCPSpellingClient and UDPSpellingClient 
 * Written by YUE CHI
 */

import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.LinkedList;
import java.util.List;

/**
 * A client class will be extended by TCP and UDP Client.
 * 
 * @author YUE CHI
 * @version 1.0
 */
public abstract class AbstractClient
{
  /**
   * The minimum valid port number.
   */
  public static final int MIN_PORT_NUMBER = 1024;

  /**
   * The maximum valid port number.
   */
  public static final int MAX_PORT_NUMBER = 65535;

  /**
   * The maximum valid packet length.
   */
  public static final int MAX_PACKET_LENGTH = 65535;

  /**
   * The maximum time to decide time out.
   */
  public static final int TIME_OUT_NUMBER = 10000;

  /** The number of input string. **/
  public static final int NUMBER_OF_INPUT = 3;

  /**
   * The String for ASC CODE.
   */
  public static final String ASC_CODE = "US-ASCII";

  /**
   * The space string.
   */
  public static final String SPACE_STRING = " ";

  /**
   * The host address.
   */
  protected InetAddress my_host;

  /**
   * The port number.
   */
  protected int my_port;

  /**
   * The list of words need to be searched.
   */
  protected List my_searchlist = new LinkedList();

  /**
   * The current number of word in the list.
   */
  protected int my_current;

  /**
   * The registry for RMI client.
   */
  protected Registry my_registery;

  /**
   * The constructor of client class.
   * 
   * @param the_args input a string array
   * @throws SocketException unexpected socket.
   */
  public AbstractClient(final String[] the_args) throws SocketException
  {
    if (the_args.length < 4)
    {
      System.err.println("Usage: java RMISpellingClient <host> <port> " +
                          "<service-name> <query-word> [query_word] ...");
      System.exit(1);
    }
    this.my_host = getaAddress(the_args);
    this.my_port = getPort(the_args);
    this.my_registery = getRegistry(the_args);
    this.my_searchlist = getList(the_args);
    this.my_current = 0; // the linkedlist start from 0
  }

  /**
   * Get the address of the host.
   * 
   * @param the_args input a string array
   * @return the host number.
   */
  private static InetAddress getaAddress(final String[] the_args)
  {
    InetAddress ihost = null;
    try
    {
      // the first string of input is the host name.
      ihost = InetAddress.getByName(the_args[0]);
    }
    catch (final IOException e)
    {
      System.err.println("hostname error");
      System.exit(1);
    }
    return ihost;
  }

  /**
   * Get the port of connection.
   * 
   * @param the_args input a string array.
   * @return the port number
   */
  private static int getPort(final String[] the_args)
  {
    int iport = 0;
    try
    {
      iport = Integer.parseInt(the_args[1]);
    }
    catch (final NumberFormatException e)
    {
      System.err.println("Port number must be numeric");
      System.exit(1);
    }
    if (iport < MIN_PORT_NUMBER || iport > MAX_PORT_NUMBER)
    {
      System.err.println("Port number must be between " + MIN_PORT_NUMBER + " and " +
                         MAX_PORT_NUMBER + " inclusive.");
      System.exit(1);
    }
    return iport;
  }

  /**
   * Try to registry the RMI on particular service.
   * @param the_args The input string for the jar file
   * @return the registry.
   */
  private static Registry getRegistry(final String[] the_args)
  {
    Registry temp = null;
    try
    {
      temp = LocateRegistry.getRegistry(the_args[0], getPort(the_args));
    }
    catch (final RemoteException e)
    {
      System.err.println("Error while locating RMI registry.");
      e.printStackTrace();
    }
    return temp;
  }

  /**
   * Get the linked list for searching word.
   * 
   * @param the_args the input string array
   * @return the word searching list
   */
  private LinkedList getList(final String[] the_args)
  {
    final LinkedList my_list = new LinkedList();
    for (int i = 3; i < the_args.length; i++)
    {
      my_list.add(the_args[i]);
    }
    return my_list;
  }
}
